package com.yc.cloud.basic.utils;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yc.cloud.basic.dto.IpLocationDto;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.val;
import lombok.extern.slf4j.Slf4j;

/**
 * 请求工具类
 * Created by macro on 2020/10/8.
 */
@Slf4j
public class RequestUtil {

    private static String BAIDU_MAP_AK = "uXrdoRO1DX74Vg9KghILzFVdSokNpPmw";

    // 使用 Caffeine 缓存，设置缓存时间为 1 天
    private static final Cache<String, IpLocationDto> locationCache = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.DAYS)
            .maximumSize(10000)
            .build();

    /**
     * 获取请求真实IP地址
     */
    public static String getRequestIp(HttpServletRequest request) {
        // 按优先级获取 IP
        String[] headers = {
                "X-Forwarded-For",
                "X-Real-IP",
                "Proxy-Client-IP",
                "WL-Proxy-Client-IP",
                "HTTP_CLIENT_IP",
                "HTTP_X_FORWARDED_FOR"
        };

        String ip = null;
        for (String header : headers) {
            ip = request.getHeader(header);
            if (isValidIp(ip)) {
                // 如果是 X-Forwarded-For，取第一个非 unknown 的 IP
                if ("X-Forwarded-For".equalsIgnoreCase(header)) {
                    String[] ips = ip.split(",");
                    for (String ipAddress : ips) {
                        ipAddress = ipAddress.trim();
                        if (isValidIp(ipAddress)) {
                            return ipAddress;
                        }
                    }
                } else {
                    return ip;
                }
            }
        }

        // 如果以上都未获取到，则使用 RemoteAddr
        ip = request.getRemoteAddr();
        if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
            // 根据网卡取本机配置的 IP
            try {
                ip = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                log.error("获取本地IP异常", e);
            }
        }

        return ip;
    }

    /**
     * 检查 IP 是否有效
     */
    private static boolean isValidIp(String ip) {
        return ip != null && ip.length() > 0 && !"unknown".equalsIgnoreCase(ip.trim());
    }

    /**
     * 获取IP地址对应的地理位置
     * 使用 Hutool HttpUtil 实现
     */
    public static IpLocationDto getLocationFromIp(String ip) {

        // 如果ip是本地ip，则返回空
        if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
            log.info("本地ip，不获取位置信息");
            return null;
        }
        // 先从缓存获取
        val cachedLocationDto = locationCache.getIfPresent(ip);
        if (cachedLocationDto != null) {
            return cachedLocationDto;
        }
        val start = System.currentTimeMillis();
        // 如果缓存未命中，则调用API获取位置信息
        var location = getIpLocationByIpApi(ip);
        // if (location == null) {
        // // 如果淘宝API获取失败，则使用ip-api.com
        // location = getIpLocationByIpApi(ip);
        // }
        if (location != null) {
            // 将获取到的位置信息存入缓存
            locationCache.put(ip, location);
        }
        log.info("getLocationFromIp cost: {}ms", System.currentTimeMillis() - start);
        return location;

    }

    /**
     * 获取IP地理位置信息
     *
     * @param ip IP地址
     * @return IP地理位置信息DTO
     */
    public static IpLocationDto getIpLocationByIpApi(String ip) {
        try {
            String url = "http://ip-api.com/json/" + ip;
            String response = HttpUtil.get(url);
            if (StrUtil.isEmpty(response)) {
                log.error("getIpLocationByIpApi response is empty: {}", response);
                return null;
            }
            if (!JSONUtil.isTypeJSON(response)) {
                log.error("getIpLocationByIpApi response is not json: {}", response);
                return null;
            }
            log.info("getIpLocationByIpApi response: {}", response);
            JSONObject data = JSONUtil.parseObj(response);
            // JSONObject data = jsonObject.getJSONObject("data");
            // 提取一些有用的数据
            IpLocationDto dto = new IpLocationDto();
            dto.setCountry(data.getStr("country"));
            dto.setCountryCode(data.getStr("countryCode"));
            dto.setRegion(data.getStr("regionName"));
            dto.setRegionCode(data.getStr("region"));
            dto.setCityCode(data.getStr("city"));
            // dto.setCityCode(data.getStr("cityCode"));
            return dto;
        } catch (Exception e) {
            log.error("Error getting location for IP: " + ip, e);
            return null;
        }
    }

    public static IpLocationDto getLocationFromBaidu(String ip) {
        try {
            String url = "https://api.map.baidu.com/location/ip";
            Map<String, Object> params = new HashMap<>();
            params.put("ip", ip);
            params.put("ak", BAIDU_MAP_AK); // 使用配置的AK
            params.put("coor", "bd09ll");

            String response = HttpUtil.get(url, params);
            log.info("getLocationFromBaidu response: {}", response);
            if (StrUtil.isEmpty(response)) {
                log.error("getLocationFromBaidu response is empty");
                return null;
            }

            JSONObject json = JSONUtil.parseObj(response);
            if (json.getInt("status") == 0) {
                JSONObject content = json.getJSONObject("content");
                JSONObject addressDetail = content.getJSONObject("address_detail");

                IpLocationDto dto = new IpLocationDto();
                dto.setIp(ip);
                dto.setCountry("中国");
                dto.setCountryCode("CN");
                dto.setRegion(addressDetail.getStr("province"));
                dto.setCity(addressDetail.getStr("city"));
                dto.setRegionCode(addressDetail.getStr("province_code"));
                dto.setCityCode(addressDetail.getStr("city_code"));
                return dto;
            } else {
                log.error("getLocationFromBaidu response is not success: {}", response);
            }
            return null;
        } catch (Exception e) {
            log.error("Error getting location from Baidu for IP: " + ip, e);
            return null;
        }
    }

    public static void main(String[] args) {
        String ip = "180.106.144.184";
        IpLocationDto location = getLocationFromBaidu(ip);
        System.out.println("Location: " + location);
    }
}
